﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Brain.Photon
{
    /// <summary>
    /// Emitts Particles on the Game World
    /// </summary>
    public class ParticleEmitter : IUpdateable
    {

        public bool ShouldUpdate { get; set; }
        /// <summary>
        /// All the Particles this Emitter can have(some might be null)
        /// </summary>
        public Particle[] Particles;

        /// <summary>
        /// A Modifier to apply to all Particles.
        /// May only work on CPU Renderers
        /// </summary>
        public ParticleModifier Modifier;

        /// <summary>
        /// The Maximum number of Particles this Emitter can have. Changing this to a new value
        /// won't change the Particles array size, so be careful
        /// </summary>
        protected int maxParticles = 0;

        /// <summary>
        /// The Maximum number of Particles this Emitter can have. Caution setting this to a new value,
        /// it will resize the Particles Array, which may be pretty much slow depending on the running device
        /// </summary>
        public int MaxParticles
        {
            get { return maxParticles; }
            set
            {
                maxParticles = value;
                Array.Resize(ref Particles, value);
            }
        }

        /// <summary>
        /// The Properties of the Particles
        /// </summary>
        public ParticleProperties Properties;

        /// <summary>
        /// How Much particles are currently active
        /// </summary>
        public int ActiveParticles { get; protected set; }

        /// <summary>
        /// The Parent of this Particle Emitter (the owner)
        /// </summary>
        public ParticleSystem Parent;

        public int FirstActiveParticle { get; protected set; }
        public int LastActiveParticle { get; protected set; }

        public event SetParticle OnSpawnParticle;

        /// <summary>
        /// The Renderer used to render this emitter
        /// </summary>
        public PhotonRenderer Renderer;

        /// <summary>
        /// Tag Reserved for Rendering purposes
        /// </summary>
        public object RenderingTag;

        /// <summary>
        /// Reserved event used by the Engine, to know when a Particle was spawned.
        /// On the actual version(12/31/2011) only the PhotonMainGPURenderer uses this
        /// </summary>
        public event SetIndexParticleNEmitter Reserved_SpawnParticle;

        /// <summary>
        /// Makes a new instance of the ParticleEmitter class
        /// </summary>
        /// <param name="MaxParticles">The Maximum amount of Particles this Emmiter initially have</param>
        public ParticleEmitter(int MaxParticles)
        {
            Particles = new Particle[MaxParticles];
            Properties = new ParticleProperties();
            this.maxParticles = MaxParticles;
        }

        public virtual void AssignRenderer(PhotonRenderer renderer)
        {
            this.Renderer = renderer;
        }

        /// <summary>
        /// Spawns a Particle at the desired Position, with the Default Velocities.
        /// </summary>
        /// <param name="centerPosition"></param>
        public virtual Particle SpawnParticle(Vector3 centerPosition)
        {
            return SpawnParticle(centerPosition, Vector3.Zero, Vector3.Zero);
        }

        /// <summary>
        /// Spawns a Particle at the desired Position with the specified velocity plus the Default Velocities
        /// </summary>
        /// <param name="centerPosition"></param>
        /// <param name="linearVelocity">The Linear Velocity of the Particle(velocity that changes position)</param>
        /// <param name="angularVelocity">The Angular Velocity of the Particle(velocity that changes rotation)</param>
        public virtual Particle SpawnParticle(Vector3 centerPosition, Vector3 linearVelocity, Vector3 angularVelocity, Color color, float Size, bool ApplyRandom)
        {
            if (ActiveParticles < Particles.Length)
            {
                Particle particle = new Particle();
                particle.Emitter = this;

                if (Renderer.IsGPURenderer)
                {
                    particle.AliveTime = (float)Engine.Instance.GameTime.ElapsedGameTime.TotalSeconds;
                }
                else
                {
                }

                if (Properties.SpawnArea != null)
                {
                    particle.Position = Properties.SpawnArea.GetParticlePosition(centerPosition);
                }
                else
                {
                    particle.Position = centerPosition;
                }

                if (ApplyRandom)
                {
                    particle.LinearVelocity = linearVelocity + RandomUtil.RandomVector3(Properties.MinRandomStartLinearVelocity, Properties.MaxRandomStartLinearVelocity, 100) + Properties.StartLinearVelocity;
                    particle.AngularVelocity = angularVelocity + RandomUtil.RandomVector3(Properties.MinRandomStartAngularVelocity, Properties.MaxRandomStartAngularVelocity, 100) + Properties.StartAngularVelocity;

                    if (Size == 0)
                    {
                        particle.Size = RandomUtil.Next(Properties.MinStartSize, Properties.MaxStartSize, 100);
                    }
                    else
                    {
                        particle.Size = Size;
                    }
                }
                else
                {
                    particle.LinearVelocity = linearVelocity;
                    particle.AngularVelocity = angularVelocity;
                    particle.Size = Size;
                }
                particle.Color = color;

                Particles[LastActiveParticle] = particle;
                ActiveParticles++;

                if (OnSpawnParticle != null)
                {
                    OnSpawnParticle(particle);
                }
                if (Reserved_SpawnParticle != null)
                {
                    Reserved_SpawnParticle(LastActiveParticle, particle, this);
                }
                LastActiveParticle++;
                if (LastActiveParticle == Particles.Length)
                {
                    LastActiveParticle = 0;
                }

                return particle;
            }
            return null;
        }

        /// <summary>
        /// Spawns a Particle at the desired Position with the specified velocity plus the Default Velocities
        /// </summary>
        /// <param name="centerPosition"></param>
        /// <param name="linearVelocity">The Linear Velocity of the Particle(velocity that changes position)</param>
        /// <param name="angularVelocity">The Angular Velocity of the Particle(velocity that changes rotation)</param>
        public virtual Particle SpawnParticle(Vector3 centerPosition, Vector3 linearVelocity, Vector3 angularVelocity)
        {
            return SpawnParticle(centerPosition, linearVelocity, angularVelocity, Color.White, 0, true);
        }

        /// <summary>
        /// Updates every Active Particle. Called by the Particle System father of this Particle Emmiter.
        /// </summary>
        public virtual void Update()
        {
            if (Renderer.IsGPURenderer)
            {
                if (Modifier != null)
                {
                    Modifier.GPU_Modify(this);
                }
            }
            else
            {
                if (!Renderer.ShouldHandleParticleUpdate)
                {
                    for (int i = 0; i < MaxParticles; i++)
                    {
                        Particle p = Particles[i];

                        if (p != null)
                        {
                            UpdateParticle(p, i);
                        }
                    }
                }
            }
        }

        public virtual void Draw()
        {
            if (Renderer != null)
            {
                Renderer.Draw();
            }
        }

        public virtual void RemoveParticle(int index)
        {
            Particles[index] = null;
            ActiveParticles--;
            FirstActiveParticle = index + 1;
            if (FirstActiveParticle >= Particles.Length)
            {
                FirstActiveParticle = 0;
            }
        }

        /// <summary>
        /// Updates a Single Particle. Returns true if the Particle was killed on this update.
        /// ONLY called when CPU Renderers are used
        /// </summary>
        /// <param name="p"></param>
        /// <param name="index"></param>
        public virtual bool UpdateParticle(Particle p, int index)
        {
            float time = ((float)Engine.Instance.GameTime.ElapsedGameTime.TotalSeconds);

            p.AliveTime += time;
            if (p.AliveTime > Properties.MaxTime)
            {
                // Particle dies here
                RemoveParticle(index);
                return true;
            }
            if (Modifier != null)
            {
                Modifier.CPU_Modify(p, this);
            }
            Vector3 linear = p.LinearVelocity - (p.LinearVelocity * Properties.LinearVelocityLoss * p.AliveTime);
            linear += (Properties.Gravity * time);
            p.LinearVelocity = linear;

            p.Rotation += p.AngularVelocity * time;
            p.Position += p.LinearVelocity * time;
            if (Renderer.CPU_NeedsTransform)
            {
                p.Transform = Matrix.CreateScale(p.Size) * Matrix.CreateTranslation(p.Position);
            }

            return false;
        }
    }
}
